package com.bayss.core.service.homepage;

import com.alibaba.fastjson.JSON;
import com.bayss.core.DokoServerException;
import com.bayss.core.DokoServerExceptionFactor;
import com.bayss.core.constant.ResultMsg;
import com.bayss.core.entity.homepage.*;
import com.bayss.core.entity.homepage.mapper.ActivityApplyCacheInfoMapper;
import com.bayss.core.entity.homepage.mapper.ActivityApplyInfoMapper;
import com.bayss.core.entity.homepage.mapper.ActivityInfoMapper;
import com.bayss.core.entity.user.TransactionRecord;
import com.bayss.core.entity.user.UserInfo;
import com.bayss.core.enums.PayOrigin;
import com.bayss.core.model.ActivityApplyCount;
import com.bayss.core.service.TransactionRecordService;
import com.bayss.core.service.pay.PayService;
import com.bayss.core.service.user.UserService;
import com.bayss.core.util.CollectionUtils;
import com.bayss.core.util.FileUtil;
import com.bayss.core.util.OrderUtils;
import com.bayss.core.util.RedisUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * program: doko
 * author: bsworld.xie
 * create: 2019-11-10 14:55
 * description:
 */
@Service
public class ActivityService {

    private static final long CACHED_EXPRED_TIME = 30 * 60 * 1000;


    private static Logger logger = LogManager.getLogger();
    @Autowired
    ActivityInfoMapper activityInfoMapper;
    @Autowired
    ActivityApplyInfoMapper activityApplyInfoMapper;

    @Autowired
    ActivityApplyCacheInfoMapper cacheMapper;
    @Autowired
    UserService userService;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    private PayService payService;
    @Autowired
    private TransactionRecordService transactionRecordService;
    /**
     * 活动新建
     */
    public ResultMsg addActivity(ActivityInfo activityInfo) {
        if (activityInfo.getStartTime() == null || activityInfo.getEndTime() == null) {
            return ResultMsg.fail(-1, "活动时间不能为空");
        }
        if (activityInfo.getPicCn() == null || activityInfo.getPicEn() == null || activityInfo.getPicKo() == null) {
            return ResultMsg.fail(-1, "活动图不能为空");
        }
        if (activityInfo.getDetailPicCn() == null || activityInfo.getDetailPicEn() == null || activityInfo.getDetailPicKo() == null) {
            return ResultMsg.fail(-1, "详情图不能为空");
        }
        activityInfo.setPicCn(FileUtil.formatImgUrl(activityInfo.getPicCn()));
        activityInfo.setPicEn(FileUtil.formatImgUrl(activityInfo.getPicEn()));
        activityInfo.setPicKo(FileUtil.formatImgUrl(activityInfo.getPicKo()));
        activityInfo.setDetailPicCn(FileUtil.formatImgUrl(activityInfo.getDetailPicCn()));
        activityInfo.setDetailPicEn(FileUtil.formatImgUrl(activityInfo.getDetailPicEn()));
        activityInfo.setDetailPicKo(FileUtil.formatImgUrl(activityInfo.getDetailPicKo()));
        activityInfoMapper.insertSelective(activityInfo);
        return ResultMsg.success();
    }

    /**
     * 活动更新
     *
     * @param activityInfo
     */
    public void updateActivity(ActivityInfo activityInfo) {
        logger.info("updateActivity(), activityInfo:{}", JSON.toJSONString(activityInfo));
        activityInfo.setPicCn(FileUtil.formatImgUrl(activityInfo.getPicCn()));
        activityInfo.setPicEn(FileUtil.formatImgUrl(activityInfo.getPicEn()));
        activityInfo.setPicKo(FileUtil.formatImgUrl(activityInfo.getPicKo()));
        activityInfo.setDetailPicCn(FileUtil.formatImgUrl(activityInfo.getDetailPicCn()));
        activityInfo.setDetailPicEn(FileUtil.formatImgUrl(activityInfo.getDetailPicEn()));
        activityInfo.setDetailPicKo(FileUtil.formatImgUrl(activityInfo.getDetailPicKo()));
        activityInfoMapper.updateByPrimaryKeySelective(activityInfo);
    }

    /**
     * 活动查询根据id
     *
     * @param id
     * @return
     */
    public ActivityInfo getActivityById(UserInfo userInfo, Integer id) {
        logger.info("getActivityById(), id:{}", id);
        ActivityInfoExample example = new ActivityInfoExample();
        example.createCriteria().andIdEqualTo(id);
        List<ActivityInfo> activityInfos = activityInfoMapper.selectByExample(example);
        if (activityInfos == null || activityInfos.size() == 0) {
            return null;
        }
        ActivityInfo activityInfo = activityInfos.get(0);

        ActivityApplyInfoExample applyExample = new ActivityApplyInfoExample();
        applyExample.createCriteria().andActivityIdEqualTo(id).andApplyStatusEqualTo(1);
        List<ActivityApplyInfo> applyInfoList = activityApplyInfoMapper.selectByExample(applyExample);
        int haveApplyNum = applyInfoList.stream().mapToInt(ActivityApplyInfo::getApplyNum).sum();
        activityInfo.setHasApplyNum(haveApplyNum);

        if (userInfo != null) {
            //登录状态时，查询当前用户是否报名过
            List<ActivityApplyInfo> list2 = applyInfoList.stream().filter(e -> userInfo.getId().intValue() == e.getUid()).collect(Collectors.toList());
            if (list2.size() > 0) {
                activityInfo.setHasApplied(1);
            }
        }
        return activityInfo;
    }

    /**
     * 后端展示所有的活动
     *
     * @param status:筛选符合条件状态的活动
     * @param isInTimeRange      确定是否查询在开始时间和结束时间内的活动
     */
    public PageInfo<ActivityInfo> getActivityByPage(Integer page, Integer pageSize, Integer status, boolean isInTimeRange) {
        ActivityInfoExample example = new ActivityInfoExample();
        example.setOrderByClause("create_time desc");
        PageHelper.startPage(page, pageSize);
        ActivityInfoExample.Criteria criteria = example.createCriteria();
        if (status != null) {
            criteria.andStatusEqualTo(1);
        }
        if (isInTimeRange) {
            long now = System.currentTimeMillis();
            criteria.andStartTimeLessThanOrEqualTo(now);
            criteria.andEndTimeGreaterThanOrEqualTo(now);
        }
        PageInfo<ActivityInfo> result = PageHelper.startPage(page, pageSize).doSelectPageInfo(() -> activityInfoMapper.selectByExample(example));
        for(ActivityInfo info:result.getList()) {
            //已报名人数
            ActivityApplyInfoExample applyExample = new ActivityApplyInfoExample();
            applyExample.createCriteria().andActivityIdEqualTo(info.getId()).andApplyStatusEqualTo(1);
            List<ActivityApplyInfo> applyInfoList = activityApplyInfoMapper.selectByExample(applyExample);
            int haveApplyNum = applyInfoList.stream().mapToInt(ActivityApplyInfo::getApplyNum).sum();
            info.setHasApplyNum(haveApplyNum);
        }
        return result;
    }

    /**
     * todo 一个用户只能参加一次活动吗
     * 活动的开始时间和结束时间是否是必须的
     */
    public ResultMsg addActivityApply(UserInfo userInfo, ActivityApplyInfo activityApplyInfo) {
        Integer activityId = activityApplyInfo.getActivityId();
        if (activityId == null) {
            return ResultMsg.fail(DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED.getErrorCode(), DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED.getErrorMsgCN());
        }
        ActivityInfo activityInfoDB = activityInfoMapper.selectByPrimaryKey(activityId);
        if (activityInfoDB == null) {
            return ResultMsg.fail(DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED.getErrorCode(), DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED.getErrorMsgCN());
        }
        if (activityInfoDB.getStatus() == 0) {
            return ResultMsg.fail(DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED.getErrorCode(), DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED.getErrorMsgCN());
        }

        Long startTime = activityInfoDB.getStartTime();
        Long endTime = activityInfoDB.getEndTime();
        Long now = System.currentTimeMillis();
        if (startTime != null && endTime != null) {
            if (now < startTime || now > endTime) {
                return ResultMsg.fail(DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED.getErrorCode(), DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED.getErrorMsgCN());
            }
        }
        String lockKey = "activity_apply_lock" + activityId + "_" + userInfo.getId();
        try {
            int LOCK_EXPIRE = 5 * 60 * 1000;//失效时间毫秒
            boolean lockActivity = redisUtil.lock(lockKey, LOCK_EXPIRE);
            if (!lockActivity) {
                return ResultMsg.fail(DokoServerExceptionFactor.ACTIVITY_APPLY_REPEAT.getErrorCode(), DokoServerExceptionFactor.ACTIVITY_APPLY_REPEAT.getErrorMsgCN());
            }

            ActivityApplyInfoExample applyExample = new ActivityApplyInfoExample();
            applyExample.createCriteria().andActivityIdEqualTo(activityId).andApplyStatusEqualTo(1).andUidEqualTo(userInfo.getId());

            List<ActivityApplyInfo> activityApplyInfosCheck = activityApplyInfoMapper.selectByExample(applyExample);
            if (CollectionUtils.isNotEmpty(activityApplyInfosCheck)) {
                return ResultMsg.fail(DokoServerExceptionFactor.ACTIVITY_APPLY_REPEAT.getErrorCode(), DokoServerExceptionFactor.ACTIVITY_APPLY_REPEAT.getErrorMsgCN());
            }

            applyExample.clear();
            Integer applyNum = activityApplyInfo.getApplyNum();
            Integer limitApplyNumber = activityInfoDB.getApplyNumber();
            if (limitApplyNumber > 0) {
                applyExample.createCriteria().andActivityIdEqualTo(activityId).andApplyStatusEqualTo(1);
                List<ActivityApplyInfo> activityApplyList = activityApplyInfoMapper.selectByExample(applyExample);
                int haveApplyNum = activityApplyList.stream().mapToInt(ActivityApplyInfo::getApplyNum).sum();
                //缓存有效期30min
                ActivityApplyCacheInfoExample cacheExample = new ActivityApplyCacheInfoExample();
                cacheExample.createCriteria().andActivityIdEqualTo(activityId).andStatusEqualTo(1).andExpiredTimeGreaterThan(now);
                List<ActivityApplyCacheInfo> activityApplyCacheInfos = cacheMapper.selectByExample(cacheExample);
                int tempApplyNumber = 0;
                if (CollectionUtils.isNotEmpty(activityApplyCacheInfos)) {
                    tempApplyNumber = activityApplyCacheInfos.stream().mapToInt(ActivityApplyCacheInfo::getApplyNumber).sum();
                }

                int usedApplyNumber = limitApplyNumber - haveApplyNum - tempApplyNumber;
                if (applyNum > usedApplyNumber) {
                    return ResultMsg.fail(DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED_BEYOND_LIMIT.getErrorCode(), DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED_BEYOND_LIMIT.getErrorMsgCN());
                }
            }

            //免费报名时
            if (activityInfoDB.getApplyFee().equals(0)) {
                ActivityApplyInfo applyInfoDB = new ActivityApplyInfo();
                applyInfoDB.setActivityId(activityId);
                applyInfoDB.setApplyNum(applyNum);
                applyInfoDB.setApplyStatus(1);
                applyInfoDB.setCreateTime(new Date());
                applyInfoDB.setName(activityApplyInfo.getName());
                applyInfoDB.setCellphone(activityApplyInfo.getCellphone());
                applyInfoDB.setPayInfo("");
                applyInfoDB.setUid(userInfo.getId());
                activityApplyInfoMapper.insertSelective(applyInfoDB);
                return ResultMsg.success();
            }

            //获取支付信息
            String orderNo = OrderUtils.getActiveNo(userInfo.getId());
            String money = String.valueOf(activityInfoDB.getApplyFee() * applyNum);
            String payInfo = null;
            try {
                payInfo = payService.payOrder(userInfo.getOpenId(), orderNo, userInfo.getId(), money, PayOrigin.ACTIVE, "");
            } catch (Exception e) {
                logger.error("支付信息失败", e);
                return ResultMsg.fail(DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED.getErrorCode(), DokoServerExceptionFactor.ACTIVITY_APPLY_FAILED.getErrorMsgCN());
            }

            ActivityApplyCacheInfo cacheInfo = new ActivityApplyCacheInfo();
            cacheInfo.setStatus(1);
            cacheInfo.setActivityId(activityId);
            cacheInfo.setApplyNumber(applyNum);
            now = System.currentTimeMillis();
            cacheInfo.setExpiredTime(now + CACHED_EXPRED_TIME);
            cacheInfo.setCreateTime(now);
            cacheInfo.setUid(userInfo.getId());
            cacheInfo.setActivityOrderNo(orderNo);
            cacheInfo.setName(activityApplyInfo.getName());
            cacheInfo.setCellphone(activityApplyInfo.getCellphone());
            cacheInfo.setPayInfo(payInfo);
            cacheMapper.insertSelective(cacheInfo);
            return ResultMsg.success(payInfo);
        } finally {
            redisUtil.releaseLock(lockKey);
        }
    }

    /**
     * 支付完成后
     */
    public void activityPayCallBack(String activityOrderNo) {
        ActivityApplyCacheInfoExample applyExample = new ActivityApplyCacheInfoExample();
        applyExample.createCriteria().andActivityOrderNoEqualTo(activityOrderNo).andStatusEqualTo(1);
        List<ActivityApplyCacheInfo> cacheInfoList = cacheMapper.selectByExample(applyExample);

        if (cacheInfoList == null || cacheInfoList.size() == 0) {
            logger.error("活动支付回调失败，activityOrderNo:{}不存在", activityOrderNo);
            return;
        }
        ActivityApplyCacheInfo cacheInfoDB = cacheInfoList.get(0);

        //创建
        ActivityApplyInfo activityApplyInfo = new ActivityApplyInfo();
        activityApplyInfo.setActivityId(cacheInfoDB.getActivityId());
        activityApplyInfo.setApplyNum(cacheInfoDB.getApplyNumber());
        activityApplyInfo.setApplyStatus(1);
        activityApplyInfo.setCreateTime(new Date(cacheInfoDB.getCreateTime()));
        activityApplyInfo.setName(cacheInfoDB.getName());
        activityApplyInfo.setCellphone(cacheInfoDB.getCellphone());
        activityApplyInfo.setPayInfo(cacheInfoDB.getPayInfo());
        activityApplyInfo.setUid(cacheInfoDB.getUid());
        activityApplyInfoMapper.insertSelective(activityApplyInfo);

        //cacheInfo无效
        cacheInfoDB.setStatus(0);
        cacheMapper.updateByPrimaryKeySelective(cacheInfoDB);

        //交易记录
        ActivityInfo activityInfoDB = activityInfoMapper.selectByPrimaryKey(cacheInfoDB.getActivityId());
        Integer money = activityInfoDB.getApplyFee() * cacheInfoDB.getApplyNumber();
        transactionRecordService.saveOrderRecord(cacheInfoDB.getUid(), money, activityOrderNo);
    }

    /**
     * 当支付成功时需要回调，删除临时缓存,释放库存
     */

    public void releaseActivityStock(Integer activityId, Long uid) {
        if (activityId == null || activityId == null) {
            return;
        }
        ActivityApplyCacheInfoExample cacheExample = new ActivityApplyCacheInfoExample();
        cacheExample.createCriteria().andUidEqualTo(uid).andActivityIdEqualTo(activityId);
        ActivityApplyCacheInfo cacheInfo = new ActivityApplyCacheInfo();
        cacheInfo.setStatus(0);
        cacheMapper.updateByExampleSelective(cacheInfo, cacheExample);
    }


    public void updateActivityApply(ActivityApplyInfo activityApplyInfo) {
        activityApplyInfoMapper.updateByPrimaryKeySelective(activityApplyInfo);
    }

    public ActivityApplyInfo getActivityApplyById(Integer id) {
        ActivityApplyInfoExample example = new ActivityApplyInfoExample();
        example.createCriteria().andIdEqualTo(id);
        List<ActivityApplyInfo> activityApplyInfos = activityApplyInfoMapper.selectByExample(example);
        if (activityApplyInfos == null || activityApplyInfos.size() == 0) {
            return null;
        }
        return activityApplyInfos.get(0);
    }

    public PageInfo<ActivityApplyInfo> findPages(Integer activityId, int page, int pageSize) {
        ActivityApplyInfoExample example = new ActivityApplyInfoExample();
        example.createCriteria().andActivityIdEqualTo(activityId).andApplyStatusEqualTo(1);
        PageInfo<ActivityApplyInfo> result = PageHelper.startPage(page, pageSize).doSelectPageInfo(() -> activityApplyInfoMapper.selectByExample(example));
        return result;
    }

    public ActivityApplyCount countApplyAmount(Integer activityId) {
        ActivityApplyInfoExample applyExample = new ActivityApplyInfoExample();
        applyExample.createCriteria().andActivityIdEqualTo(activityId).andApplyStatusEqualTo(1);
        List<ActivityApplyInfo> applyInfoList = activityApplyInfoMapper.selectByExample(applyExample);
        int haveApplyNum = applyInfoList.stream().mapToInt(ActivityApplyInfo::getApplyNum).sum();

        ActivityInfo activityInfo = activityInfoMapper.selectByPrimaryKey(activityId);
        int fee = activityInfo.getApplyFee();
        int totalAmount = fee * haveApplyNum;

        return ActivityApplyCount.builder().totalAmount(totalAmount).totalNum(haveApplyNum).build();
    }
    public void deleteByPrimaryKey(Integer id) {
        activityInfoMapper.deleteByPrimaryKey(id);
    }
}
